package com.coocaa.ops.admin.v1.upload.ftp;
import com.coocaa.ops.admin.v1.upload.exception.FtpDelException;
import com.coocaa.ops.admin.v1.upload.exception.FtpDownloadException;
import com.coocaa.ops.admin.v1.upload.exception.FtpException;
import com.coocaa.ops.admin.v1.upload.exception.FtpUploadException;
import com.coocaa.ops.admin.v1.upload.ftp.model.FtpConfig;
import com.coocaa.ops.admin.v1.upload.ftp.model.ProgressMonitor;
import com.coocaa.ops.admin.v1.upload.properties.FtpProperties;
import com.coocaa.ops.common.tool.exception.ExceptionUtil;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

/**
 * @author bijiahao
 * @date : 2019/2/14.
 * @description
 */
public class MyFtpTemplate extends AbstractFtpClient implements IFtpOperations {
    private final static Logger log = LoggerFactory.getLogger(MyFtpTemplate.class);

    public MyFtpTemplate() {
        init();
    }

    private void init() {
        //ftp连接
        try {
            this.connect();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void upload(String localFilePath, String remoteFilePath) throws FtpUploadException {
        upload(localFilePath, remoteFilePath, null);
    }

    @Override
    public void upload(String localFilePath, String remoteFilePath, ProgressMonitor progressMonitor) throws FtpUploadException {
        File localFile = new File(localFilePath);
        upload(localFile, remoteFilePath, progressMonitor);
    }

    @Override
    public void upload(File localFile, String remoteFilePath) throws FtpUploadException {
        upload(localFile, remoteFilePath, null);
    }

    @Override
    public void upload(File localFile, String remoteFilePath, ProgressMonitor progressMonitor) throws FtpUploadException {
        try {
            // 设置被动模式
            ftpClient.enterLocalPassiveMode();
            // 设置以二进制方式传输
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            //获取编码后的ftp路径
            remoteFilePath = getEncodingFtpPath(remoteFilePath);
            FTPFile[] ftpFiles = listFtpFile(remoteFilePath);
            long remoteFileSize = 0;
            if (ftpFiles.length == 1) {
                remoteFileSize = ftpFiles[0].getSize();
            }
            //创建目录
            this.createDirectoty(remoteFilePath);
            handleUploadFile(localFile, remoteFilePath, remoteFileSize, progressMonitor);
        } catch (IOException e) {
            log.error(ExceptionUtil.getStackTraceAsString(e));
            throw new FtpUploadException("ftp upload error:" + e.getMessage() , e);
        } finally {
            this.disconnect();
        }
    }

    /**
     * 处理上传
     *
     * @param localFile
     * @param remoteFilePath
     * @param progressMonitor
     * @throws IOException
     */
    protected void handleUploadFile(File localFile, String remoteFilePath, long remoteFileSize, ProgressMonitor progressMonitor) throws IOException {
        long fileLength = localFile.length();
        if (fileLength == remoteFileSize) {
            System.out.printf("file has exist");
        }
        RandomAccessFile raf = new RandomAccessFile(localFile, "r");
        if (remoteFileSize > 0) {
            raf.seek(remoteFileSize);
            ftpClient.setRestartOffset(remoteFileSize);
            if (null != progressMonitor) {
                progressMonitor.setCompleted(remoteFileSize);
            }
        }
        int byteSize = getByteSize(fileLength);
        ftpClient.setBufferSize(byteSize);
        OutputStream out = ftpClient.storeFileStream(remoteFilePath);
        int progress = 0;
        int c;
        byte[] bytes = new byte[byteSize];
        while ((c = raf.read(bytes)) != -1) {
            out.write(bytes, 0, c);
            if (null != progressMonitor) {
                progressMonitor.setTotal(fileLength);
                progressMonitor.updateCompleted(c);
                int nowProcess = progressMonitor.getProgress();
                if (nowProcess > progress) {
                    progress = nowProcess;
                    log.debug("====上传进度：" + progress);
                }
            }
        }
        out.flush();
        raf.close();
        out.close();
        boolean isDo = ftpClient.completePendingCommand();
        if (!isDo) {
            throw new FtpUploadException("ftp upload completed fail");
        }
    }


    @Override
    public void download(String remoteFilePath, String localFilePath) throws FtpDownloadException {
        download(remoteFilePath, localFilePath, null);
    }

    @Override
    public void download(String remoteFilePath, String localFilePath, ProgressMonitor progressMonitor) throws FtpDownloadException {
        try {
            // 设置被动模式
            ftpClient.enterLocalPassiveMode();
            // 设置以二进制方式传输
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            FTPFile[] ftpFiles = listFtpFile(remoteFilePath);
            if (ftpFiles.length != 1) {
                throw new FtpDownloadException("ftp file not exist");
            }
            File localFile = new File(localFilePath);
            long remoteFileSize = ftpFiles[0].getSize();
            handleDownloadFile(localFile, remoteFilePath, remoteFileSize, progressMonitor);
        } catch (IOException e) {
            throw new FtpDownloadException("ftp download error", e);
        } finally {
            this.disconnect();
        }
    }

    /**
     * 处理下载
     *
     * @param localFile
     * @param remoteFilePath
     * @param remoteFileSize
     * @param progressMonitor
     * @throws IOException
     */
    private void handleDownloadFile(File localFile, String remoteFilePath, long remoteFileSize, ProgressMonitor progressMonitor) throws IOException {
        FileOutputStream out;
        if (localFile.exists()) {
            long localSize = localFile.length();
            if (null != progressMonitor) {
                progressMonitor.setCompleted(localSize);
            }
            out = new FileOutputStream(localFile, true);
            ftpClient.setRestartOffset(localSize);
        } else {
            out = new FileOutputStream(localFile);
        }
        int byteSize = getByteSize(remoteFileSize);
        ftpClient.setBufferSize(byteSize);
        InputStream in = ftpClient.retrieveFileStream(remoteFilePath);
        int c;
        byte[] bytes = new byte[byteSize];
        int progress = 0;
        while ((c = in.read(bytes)) != -1) {
            out.write(bytes, 0, c);
            if (null != progressMonitor) {
                progressMonitor.setTotal(remoteFileSize);
                progressMonitor.updateCompleted(c);
                int nowProcess = progressMonitor.getProgress();
                if (nowProcess > progress) {
                    progress = nowProcess;
                    log.debug("====下载进度：" + progress);
                }
            }
        }
        out.flush();
        out.close();
        in.close();
        boolean isDo = ftpClient.completePendingCommand();
        if (!isDo) {
            throw new FtpUploadException("ftp download completed fail");
        }
    }


    @Override
    public void deleteFtpFile(String ftpFilePath) throws FtpDelException {
        boolean isDo = false;
        try {
            isDo = ftpClient.deleteFile(ftpFilePath);
        } catch (IOException e) {
            throw new FtpDelException("ftp del file error", e);
        }
        if (!isDo) {
            throw new FtpDelException("ftp del file fail");
        }
    }

    @Override
    public FTPFile[] listFtpFile(String ftpFilePath) throws FtpException {
        try {
            return ftpClient.listFiles(ftpFilePath);
        } catch (IOException e) {
            throw new FtpException(e);
        }
    }

    @Override
    protected FtpConfig getFtpConfig() {
        FtpConfig ftpConfig = new FtpConfig();
        ftpConfig.setFtpHost(FtpProperties.Constants.FTP_HOST);
        ftpConfig.setPort(FtpProperties.Constants.FTP_PORT);
        ftpConfig.setUsername(FtpProperties.Constants.FTP_USERNAME);
        ftpConfig.setPassword(FtpProperties.Constants.FTP_PASSWORD);
        return ftpConfig;
    }

    public static void main(String[] args) {

    }
}
